package com.zjc.app.api.sdk.client;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.zjc.app.api.sdk.Constants.Constants;
import com.zjc.app.api.sdk.dto.FileResponse;
import com.zjc.app.api.sdk.dto.ResultDto;
import com.zjc.app.api.sdk.exception.AppBizRuntimeException;
import com.zjc.app.api.sdk.utils.SignatureUtils;
import com.zjc.app.api.sdk.utils.StringUtils;
import com.zjc.app.api.sdk.utils.WebUtils;

import java.io.IOException;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class BaseClient {

    public static SignatureUtils.SignatureMethod signMethod = SignatureUtils.SignatureMethod.valueOf(Constants.SIGN_TYPE_HMAC_SHA256);
    public static final Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    private String serverUrl;
    private String appId;
    private String appSecret;
    private int connectTimeout = 3000;
    private int readTimeout = 15000;

    public BaseClient(String serverUrl, String appId, String appSecret, Integer connectTimeout, Integer readTimeout) {
        this.setServerUrl(serverUrl);
        this.setAppId(appId);
        this.setAppSecret(appSecret);
        if (connectTimeout != null) {
            this.setConnectTimeout(connectTimeout);
        }
        if (readTimeout != null) {
            this.setReadTimeout(readTimeout);
        }
    }

    /**
     * 清除安全设置
     */
    static {
        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
    }

    public Map<String, String> signHeaders(Object obj) {
        //时间戳
        String timestamp = String.valueOf(System.currentTimeMillis());
        //进行签名
        Map<String, String> params = new TreeMap<>();
        params.put(Constants.APP_ID, getAppId());
        params.put(Constants.TIMESTAMP, timestamp);
        params.put(Constants.SIGN_TYPE, signMethod.name());
        params.put(Constants.PAYLOAD, obj != null ? gson.toJson(obj) : "");
        String sign = SignatureUtils.sign(signMethod, getAppSecret(), params);
        //设置请求头
        Map<String, String> headers = new HashMap<>(3);
        headers.put(Constants.HEADER_APP_ID, getAppId());
        headers.put(Constants.HEADER_TIMESTAMP, timestamp);
        headers.put(Constants.HEADER_SIGN, sign);
        return headers;
    }

    public String pathParam(String uri, Map<String, String> params) {
        if (params != null && !params.isEmpty()) {
            StringBuffer buffer = new StringBuffer();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if (StringUtils.isEmpty(buffer.toString())) {
                    buffer.append("?");
                } else {
                    buffer.append("&");
                }
                buffer.append(entry.getKey()).append("=");
                if (entry.getValue() != null) {
                    buffer.append(entry.getValue());
                }
            }
            uri += buffer.toString();
        }
        return uri;
    }

    /**
     * get请求
     *
     * @param url
     * @param params
     * @return
     * @throws IOException
     * @throws AppBizRuntimeException
     */
    @SuppressWarnings("unchecked")
    public <T> ResultDto<T> doGet(String url, Map<String, String> params, TypeToken<ResultDto<T>> typeToken) throws IOException, AppBizRuntimeException {
        String uri = String.format("%s%s", getServerUrl(), url);
        Map<String, String> headers = signHeaders(params);
        uri = pathParam(uri, params);
        String res = WebUtils.doGet(uri, headers, Constants.CHARSET_UTF8, getConnectTimeout(), getReadTimeout());
        if (typeToken == null) {
            return gson.fromJson(res, ResultDto.class);
        } else {
            return gson.fromJson(res, typeToken.getType());
        }
    }

    /**
     * post请求
     *
     * @param url
     * @param bodyObject
     * @return
     * @throws IOException
     * @throws AppBizRuntimeException
     */
    @SuppressWarnings("unchecked")
    public <T> ResultDto<T> doPost(String url, Object bodyObject, TypeToken<ResultDto<T>> typeToken) throws IOException, AppBizRuntimeException {
        String uri = String.format("%s%s", getServerUrl(), url);
        Map<String, String> headers = signHeaders(bodyObject);
        String payload = bodyObject != null ? gson.toJson(bodyObject) : null;
        String res = WebUtils.doPost(uri, headers, payload, Constants.CHARSET_UTF8, getConnectTimeout(), getReadTimeout());
        if (typeToken == null) {
            return gson.fromJson(res, ResultDto.class);
        } else {
            return gson.fromJson(res, typeToken.getType());
        }
    }


    /**
     * put请求
     *
     * @param url
     * @param bodyObject
     * @return
     * @throws IOException
     * @throws AppBizRuntimeException
     */
    @SuppressWarnings("unchecked")
    public <T> ResultDto<T> doPut(String url, Object bodyObject, TypeToken<ResultDto<T>> typeToken) throws IOException, AppBizRuntimeException {
        String uri = String.format("%s%s", getServerUrl(), url);
        Map<String, String> headers = signHeaders(bodyObject);
        String payload = bodyObject != null ? gson.toJson(bodyObject) : null;
        String res = WebUtils.doPut(uri, headers, payload, Constants.CHARSET_UTF8, getConnectTimeout(), getReadTimeout());
        if (typeToken == null) {
            return gson.fromJson(res, ResultDto.class);
        } else {
            return gson.fromJson(res, typeToken.getType());
        }
    }

    /**
     * 下载post请求
     *
     * @param url
     * @param bodyObject
     * @return
     * @throws IOException
     * @throws AppBizRuntimeException
     */
    public FileResponse downloadByPost(String url, Object bodyObject) throws IOException, AppBizRuntimeException {
        String uri = String.format("%s%s", getServerUrl(), url);
        Map<String, String> headers = signHeaders(bodyObject);
        String payload = gson.toJson(bodyObject);
        return WebUtils.downloadByPost(uri, headers, payload, Constants.CHARSET_UTF8, getConnectTimeout(), getReadTimeout());
    }

    public String getServerUrl() {
        return serverUrl;
    }

    public void setServerUrl(String serverUrl) {
        this.serverUrl = serverUrl;
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public void setAppSecret(String appSecret) {
        this.appSecret = appSecret;
    }

    public int getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public int getReadTimeout() {
        return readTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }
}
